Правила, прочитайте внимательно:
@miptstats_ds22_bot. Для начала работы с ботом каждый раз отправляйте /start. Работы, присланные иным способом, не принимаются.ipynb и полученные данные (подробности далее). ipynb прислать нужно также ноутбук, сконвертированный в формате html, который можно получить как File -> Download as -> HTML. Внимательно проверьте, что plotly-графики в ней сохранились.Баллы за задание:
Легкая часть (достаточно на "хор"):
Сложная часть (необходимо на "отл"):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='whitegrid', font_scale=1.3, palette='Set2')
%matplotlib inline
import requests
from time import sleep
import json
Второй курс — самое время задуматься о будущей профессии и проанализировать существующие предложения. Дело тут даже не в том, чтобы найти интересную стажировку. В первую очередь сейчас стоит подумать о том, в какую сторону развиваться дальше. Например, если вы хотите работать в какой-либо конкретной профессии, то наверняка стоит развивать какие-то определенные навыки, и даже выбрать подходящую кафедру. Анализ существующих вакансий поможет определить, какие навыки вам нужны.
В данном задании вам нужно проанализировать вакансии на сайте hh.ru с использованием официального API.
Внимание! При работе с API не забывайте делать паузы между запросами, чтобы не задудосить сервер HeadHunter. Если вас заблокируют, это не будет являться уважительной причиной переноса дедлайна.
Мы будем работать только с вакансиями. Для этого не требуется регистрироваться и получать токен. Ниже приведен краткий пример работы с API. Подробное описание работы с вакансиями, включая параметры запросов и формат ответа можно почитать в документации.
Например, мы хотим найти вакансии по запросу Data Scientist в Москве. Тогда первую страницу поиска из 10 вакансий на страницу мы можем получить с запроса к API:
URL = 'https://api.hh.ru/vacancies'
params = {
'text': "Data Scientist",
'area': 1,
'page': 0,
'per_page': 10
}
req = requests.get(URL, params)
data = json.loads(req.content.decode())
Если все прошло успешно, полученный словарь будет иметь следующие ключи
data.keys()
dict_keys(['items', 'found', 'pages', 'per_page', 'page', 'clusters', 'arguments', 'alternate_url'])
Можем посмотреть на краткое описание первой вакансии
data['items'][0]
{'id': '49423067',
'premium': False,
'name': 'Data Scientist',
'department': None,
'has_test': False,
'response_letter_required': False,
'area': {'id': '1', 'name': 'Москва', 'url': 'https://api.hh.ru/areas/1'},
'salary': {'from': None, 'to': 320000, 'currency': 'RUR', 'gross': False},
'type': {'id': 'open', 'name': 'Открытая'},
'address': None,
'response_url': None,
'sort_point_distance': None,
'published_at': '2022-03-11T12:37:57+0300',
'created_at': '2022-03-11T12:37:57+0300',
'archived': False,
'apply_alternate_url': 'https://hh.ru/applicant/vacancy_response?vacancyId=49423067',
'insider_interview': None,
'url': 'https://api.hh.ru/vacancies/49423067?host=hh.ru',
'alternate_url': 'https://hh.ru/vacancy/49423067',
'relations': [],
'employer': {'id': '2324020',
'name': 'Точка',
'url': 'https://api.hh.ru/employers/2324020',
'alternate_url': 'https://hh.ru/employer/2324020',
'logo_urls': {'240': 'https://hhcdn.ru/employer-logo/3414734.jpeg',
'90': 'https://hhcdn.ru/employer-logo/3414733.jpeg',
'original': 'https://hhcdn.ru/employer-logo-original/743443.jpg'},
'vacancies_url': 'https://api.hh.ru/vacancies?employer_id=2324020',
'trusted': True},
'snippet': {'requirement': 'Есть опыт работы с методами машинного обучения и основными инструментами <highlighttext>data</highlighttext> science: Scikit-learn, XGBoost, CatBoost и т.п. ',
'responsibility': 'Придумывать и проверять гипотезы, которые позволят построить новые скоринговые модели и улучшить текущие. В результате они должны определять благонадёжность компаний...'},
'contacts': None,
'schedule': {'id': 'fullDay', 'name': 'Полный день'},
'working_days': [],
'working_time_intervals': [],
'working_time_modes': [],
'accept_temporary': False}
Сколько всего найдено вакансий
data['found']
318
Количество страниц в результатах поиска
data['pages']
32
Из результатов можем сделать удобную таблицу, причем в дальнейшем можно оставить только те колонки, которые необходимы для анализа.
df = pd.json_normalize(data['items'])
df.head()
| id | premium | name | department | has_test | response_letter_required | address | response_url | sort_point_distance | published_at | ... | address.metro.line_id | address.metro.lat | address.metro.lng | address.metro_stations | address.id | employer.logo_urls | salary | address.metro | department.id | department.name | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 49423067 | False | Data Scientist | NaN | False | False | NaN | None | None | 2022-03-11T12:37:57+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 53692325 | False | Junior Data Scientist (олимпиадные задачи по а... | NaN | False | False | NaN | None | None | 2022-03-11T14:28:53+0300 | ... | 97 | 55.749100 | 37.539500 | [{'station_name': 'Деловой центр', 'line_name'... | 7401404 | NaN | NaN | NaN | NaN | NaN |
| 2 | 53211742 | False | Junior Data scientist/ Младший риск-аналитик | NaN | False | True | NaN | None | None | 2022-03-13T11:16:44+0300 | ... | 2 | 55.789704 | 37.558212 | [{'station_name': 'Динамо', 'line_name': 'Замо... | 1551673 | NaN | NaN | NaN | NaN | NaN |
| 3 | 53092406 | False | Data scientist | NaN | False | False | NaN | None | None | 2022-03-12T17:01:36+0300 | ... | NaN | NaN | NaN | [] | 953346 | NaN | NaN | NaN | NaN | NaN |
| 4 | 50138376 | False | Data engineer/ Инженер по данным (удаленно) | NaN | False | False | NaN | None | None | 2022-03-11T18:39:01+0300 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | bil-4934-it | билайн: ИТ и Digital |
5 rows × 64 columns
Для получения полного описания вакансии потребуется задать отдельный запрос, используя ее id.
vacancy = df['id'].iloc[0]
vacancy_url = f'https://api.hh.ru/vacancies/{vacancy}'
req = requests.get(vacancy_url)
vacancy_info = json.loads(req.content.decode())
vacancy_info
{'id': '49423067',
'premium': False,
'billing_type': {'id': 'standard_plus', 'name': 'Стандарт плюс'},
'relations': [],
'name': 'Data Scientist',
'insider_interview': None,
'response_letter_required': False,
'area': {'id': '1', 'name': 'Москва', 'url': 'https://api.hh.ru/areas/1'},
'salary': {'from': None, 'to': 320000, 'currency': 'RUR', 'gross': False},
'type': {'id': 'open', 'name': 'Открытая'},
'address': None,
'allow_messages': True,
'site': {'id': 'hh', 'name': 'hh.ru'},
'experience': {'id': 'between3And6', 'name': 'От 3 до 6 лет'},
'schedule': {'id': 'fullDay', 'name': 'Полный день'},
'employment': {'id': 'full', 'name': 'Полная занятость'},
'department': None,
'contacts': None,
'description': '<p>Ищем специалиста по Data Science, чтобы строить и улучшать модели машинного обучения, которые помогут усовершенствовать наш продукт и внутренние процессы. Мы стремимся к тому, чтобы мы стали быстрее и удобнее для клиентов. И в этом нам нужна твоя помощь.</p> <p><strong>Что нужно делать</strong></p> <p>Придумывать и проверять гипотезы, которые позволят построить новые скоринговые модели и улучшить текущие. В результате они должны определять благонадёжность компаний по данным о бизнесе.</p> <p>Ты будешь доставать данные из баз и других источников, приводить их к нужному виду, анализировать и затем строить модели. Тебе предстоит планировать и ставить эксперименты, анализировать результаты и их статистическую значимость. Использовать feature engineering, model selection и fine tuning.</p> <p><strong>Ты подойдешь, если</strong></p> <ul> <li> <p>Знаешь Python, умеешь обрабатывать данные с помощью pandas, NumPy, Plotly.</p> </li> <li> <p>У тебя есть опыт работы с SQL.</p> </li> <li> <p>Есть опыт работы с методами машинного обучения и основными инструментами data science: Scikit-learn, XGBoost, CatBoost и т.п.</p> </li> <li> <p>Разбираешься в математической статистике, теории вероятностей и знаешь, как проводить A/B тестирования.</p> </li> </ul> <p><strong>Будет плюсом, если</strong></p> <ul> <li> <p>Ты понимаешь принципы работы нейросетей и уже имеешь опыт с одним или несколькими фреймворками для глубокого обучения: TensorFlow или PyTorch.</p> </li> <li> <p>Умеешь вводить модели машинного обучения в работу production.</p> </li> </ul> <p><strong>Что будет твоим в Точке</strong></p> <ul> <li> <p>Зарплата от 100 000 ₽. Расскажи на собеседовании, какая зарплата будет комфортной для тебя. А мы определим твой грейд по хард и софт скиллам и назовём точную сумму.</p> </li> <li> <p>Официальное трудоустройство и бесплатная страховка здоровья даже за границей.</p> </li> <li> <p>Удобный график: гибкое начало и окончание рабочего дня.</p> </li> <li> <p>Современный и уютный офис со спортзалом и библиотекой, кофе-поинтами, кухнями и зонами отдыха.</p> </li> <li> <p>Гибкие методы управления: мы используем Agile и SCRUM, чтобы каждый день создавать новое и быстро реагировать на изменения.</p> </li> <li> <p>Бесплатное корпоративное обучение: мы ездим на IT-конференции, собираемся на митапы, раздаем призы на хакатонах и проходим курсы за счёт компании.</p> </li> <li> <p>Возможность принимать решения без тысячи согласований и предлагать идеи: от простых до самых амбициозных.</p> </li> </ul> <p>И всё это в комфортных условиях без бюрократии, дресс-кода и начальников.</p>',
'branded_description': '\n<style>\n.tmpl_hh_wrapper p,\n.tmpl_hh_wrapper a,\n.tmpl_hh_wrapper img,\n.tmpl_hh_wrapper ol,\n.tmpl_hh_wrapper ul,\n.tmpl_hh_wrapper li {\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font: inherit;\n vertical-align: baseline;\n}\n\n.hht-vacancydescription {\n padding: 0px;\n}\n\n.tmpl_hh_wrapper .l-cell,\n.tmpl_hh_wrapper .l-paddings {\n padding: 0px !important;\n}\n\n.tmpl_hh_wrapper .b-vacancy-desc-wrapper {\n margin-top: 0px !important; \n}\n\n.tmpl_hh_wrapper .b-vacancy-desc {\n overflow: visible !important;\n line-height: inherit;\n}\n\n.tmpl_hh_content ol li b,\n.tmpl_hh_content ol li strong,\n.tmpl_hh_content ol li p b,\n.tmpl_hh_content ol li p strong,\n.tmpl_hh_content ul li b,\n.tmpl_hh_content ul li strong,\n.tmpl_hh_content ul li p b,\n.tmpl_hh_content ul li p strong {\n font-weight: normal;\n font-size: inherit !important;\n color: inherit !important;\n margin: 0 !important;\n text-transform: none;\n line-height: inherit;\n}\n\n.tmpl_hh_content ol li p,\n.tmpl_hh_content ul li p {\n font-weight: normal;\n margin: 0;\n}\n\n.tmpl_hh_content p b,\n.tmpl_hh_content p strong {\n margin: 0 0 0;\n padding: 28px 0 8px;\n}\n\n.tmpl_hh_wrapper {\n width: 100%;\n margin: 0 auto;\n max-width: 690px;\n position: relative;\n word-break: normal;\n color: #333333;\n overflow: hidden;\n font-family: \'Verdana\', sans-serif;\n font-size: 14px;\n line-height: 20px;\n -webkit-font-smoothing: antialiased;\n}\n\n.tmpl_hh_wrapper img {\n width: 100%;\n display: block;\n}\n\n.tmpl_hh_content {\n padding: 41px 40px 34px;\n position: relative;\n z-index: 1;\n}\n\n.tmpl_hh_content p:first-child,\n.tmpl_hh_content>strong:first-child,\n.tmpl_hh_content div>strong:first-child {\n margin-top: 0 !important;\n}\n\n.tmpl_hh_content p,\n.tmpl_hh_content b,\n.tmpl_hh_content strong {\n margin: 12px 0 12px;\n}\n\n.tmpl_hh_content b, \n.tmpl_hh_content strong {\n display: inline-block;\n font-weight: bold;\n font-size: 24px;\n line-height: 32px;\n}\n\n.tmpl_hh_content ol,\n.tmpl_hh_content ul {\n margin-left: 15px;\n list-style: none !important;\n}\n\n.tmpl_hh_content ol {\n counter-reset: list_counter;\n}\n\n.tmpl_hh_content li {\n position: relative;\n margin-bottom: 12px;\n}\n\n.tmpl_hh_content ul ul,\n.tmpl_hh_content ul ol,\n.tmpl_hh_content ol ol,\n.tmpl_hh_content ol ul {\n margin-top: 12px;\n}\n\n.tmpl_hh_content ol>li {\n counter-increment: list_counter; \n}\n\n.tmpl_hh_content li:before {\n position: absolute;\n left: -15px;\n top: 0;\n}\n\n.tmpl_hh_content ul>li:before {\n content: "";\n height: 5px;\n width: 5px;\n background: #333333;\n opacity: 0.45;\n border-radius: 50%;\n top: 7px;\n}\n\n.tmpl_hh_content ol>li:before {\n content: counter(list_counter)\'.\';\n left: auto;\n right: 100%;\n margin-right: 2px;\n}\n\n.tmpl_hh_header {\n padding: 6.5% 40px 0;\n background: url(https://hhcdn.ru/ichameleon/152526.png) 0 0 no-repeat;\n background-size: 21% auto;\n}\n\n.tmpl_hh_header_logo {\n position: relative;\n width: 28.6%;\n}\n\n.tmpl_hh_footer {\n position: relative;\n background: url(https://hhcdn.ru/ichameleon/152638.jpg);\n height: 0;\n padding-bottom: 56.4%;\n background-size: 100%;\n background-repeat: no-repeat;\n}\n\np.tmpl_hh_foonote {\n position: absolute;\n bottom: 10px;\n left: 4.6%;\n font-size: 9px;\n line-height: 9px;\n z-index: 1;\n}\n\n@media (max-width: 699px) {\n .tmpl_hh_content p b, .tmpl_hh_content p strong {\n padding: 18px 0 3px;\n }\n\n .tmpl_hh_content b, .tmpl_hh_content strong {\n font-size: 20px;\n line-height: 28px;\n }\n\n .tmpl_hh_header {\n padding: 12.9% 0 0% 0%;\n background: url(https://hhcdn.ru/ichameleon/152842.png) 0 0 no-repeat;\n background-size: 26% auto;\n }\n\n .tmpl_hh_header_logo {\n width: 50.2%; \n }\n\n .tmpl_hh_content {\n padding: 43px 0% 0px;\n }\n\n .tmpl_hh_footer {\n background: url(https://hhcdn.ru/ichameleon/152640.jpg);\n padding-bottom: 129.4%;\n background-size: 106%;\n background-repeat: no-repeat;\n margin-top: -4%;\n }\n\n p.tmpl_hh_foonote {\n position: relative;\n top: 0;\n left: 0;\n margin-top: 2%;\n }\n}\n</style>\n\n\n<div class="tmpl_hh_wrapper">\n <div class="tmpl_hh_header">\n <div class="tmpl_hh_header_logo"><img src="https://hhcdn.ru/ichameleon/152525.svg" alt=""></div>\n </div>\n <div class="tmpl_hh_content">\n <p>Ищем специалиста по Data Science, чтобы строить и улучшать модели машинного обучения, которые помогут усовершенствовать наш продукт и внутренние процессы. Мы стремимся к тому, чтобы мы стали быстрее и удобнее для клиентов. И в этом нам нужна твоя помощь.</p> <p><strong>Что нужно делать</strong></p> <p>Придумывать и проверять гипотезы, которые позволят построить новые скоринговые модели и улучшить текущие. В результате они должны определять благонадёжность компаний по данным о бизнесе.</p> <p>Ты будешь доставать данные из баз и других источников, приводить их к нужному виду, анализировать и затем строить модели. Тебе предстоит планировать и ставить эксперименты, анализировать результаты и их статистическую значимость. Использовать feature engineering, model selection и fine tuning.</p> <p><strong>Ты подойдешь, если</strong></p> <ul> <li> <p>Знаешь Python, умеешь обрабатывать данные с помощью pandas, NumPy, Plotly.</p> </li> <li> <p>У тебя есть опыт работы с SQL.</p> </li> <li> <p>Есть опыт работы с методами машинного обучения и основными инструментами data science: Scikit-learn, XGBoost, CatBoost и т.п.</p> </li> <li> <p>Разбираешься в математической статистике, теории вероятностей и знаешь, как проводить A/B тестирования.</p> </li> </ul> <p><strong>Будет плюсом, если</strong></p> <ul> <li> <p>Ты понимаешь принципы работы нейросетей и уже имеешь опыт с одним или несколькими фреймворками для глубокого обучения: TensorFlow или PyTorch.</p> </li> <li> <p>Умеешь вводить модели машинного обучения в работу production.</p> </li> </ul> <p><strong>Что будет твоим в Точке</strong></p> <ul> <li> <p>Зарплата от 100 000 ₽. Расскажи на собеседовании, какая зарплата будет комфортной для тебя. А мы определим твой грейд по хард и софт скиллам и назовём точную сумму.</p> </li> <li> <p>Официальное трудоустройство и бесплатная страховка здоровья даже за границей.</p> </li> <li> <p>Удобный график: гибкое начало и окончание рабочего дня.</p> </li> <li> <p>Современный и уютный офис со спортзалом и библиотекой, кофе-поинтами, кухнями и зонами отдыха.</p> </li> <li> <p>Гибкие методы управления: мы используем Agile и SCRUM, чтобы каждый день создавать новое и быстро реагировать на изменения.</p> </li> <li> <p>Бесплатное корпоративное обучение: мы ездим на IT-конференции, собираемся на митапы, раздаем призы на хакатонах и проходим курсы за счёт компании.</p> </li> <li> <p>Возможность принимать решения без тысячи согласований и предлагать идеи: от простых до самых амбициозных.</p> </li> </ul> <p>И всё это в комфортных условиях без бюрократии, дресс-кода и начальников.</p></div>\n <div class="tmpl_hh_footer"></div>\n <p class="tmpl_hh_foonote">АО\xa0«Точка»</p>\n </div>\n',
'vacancy_constructor_template': None,
'key_skills': [{'name': 'аналитика'},
{'name': 'SQL'},
{'name': 'Python'},
{'name': 'Информационные технологии'},
{'name': 'Работа с базами данных'},
{'name': 'Математический анализ'},
{'name': 'NumPy'},
{'name': 'Математическая статистика'},
{'name': 'XGBoost'}],
'accept_handicapped': False,
'accept_kids': False,
'archived': False,
'response_url': None,
'specializations': [{'id': '17.751',
'name': 'Другое',
'profarea_id': '17',
'profarea_name': 'Продажи'},
{'id': '1.25',
'name': 'Аналитик',
'profarea_id': '1',
'profarea_name': 'Информационные технологии, интернет, телеком'},
{'id': '3.26',
'name': 'Аналитик',
'profarea_id': '3',
'profarea_name': 'Маркетинг, реклама, PR'},
{'id': '12.746',
'name': 'Другое',
'profarea_id': '12',
'profarea_name': 'Консультирование'}],
'professional_roles': [{'id': '10', 'name': 'Аналитик'}],
'code': None,
'hidden': False,
'quick_responses_allowed': False,
'driver_license_types': [],
'accept_incomplete_resumes': False,
'employer': {'id': '2324020',
'name': 'Точка',
'url': 'https://api.hh.ru/employers/2324020',
'alternate_url': 'https://hh.ru/employer/2324020',
'logo_urls': {'240': 'https://hhcdn.ru/employer-logo/3414734.jpeg',
'90': 'https://hhcdn.ru/employer-logo/3414733.jpeg',
'original': 'https://hhcdn.ru/employer-logo-original/743443.jpg'},
'vacancies_url': 'https://api.hh.ru/vacancies?employer_id=2324020',
'trusted': True},
'published_at': '2022-03-11T12:37:57+0300',
'created_at': '2022-03-11T12:37:57+0300',
'negotiations_url': None,
'suitable_resumes_url': None,
'apply_alternate_url': 'https://hh.ru/applicant/vacancy_response?vacancyId=49423067',
'has_test': False,
'test': None,
'alternate_url': 'https://hh.ru/vacancy/49423067',
'working_days': [],
'working_time_intervals': [],
'working_time_modes': [],
'accept_temporary': False}
import sys
!{sys.executable} -m pip install openpyxl
Collecting openpyxl
Using cached openpyxl-3.0.9-py2.py3-none-any.whl (242 kB)
Collecting et-xmlfile
Using cached et_xmlfile-1.1.0-py3-none-any.whl (4.7 kB)
Installing collected packages: et-xmlfile, openpyxl
Successfully installed et-xmlfile-1.1.0 openpyxl-3.0.9
WARNING: You are using pip version 21.3.1; however, version 22.0.4 is available.
You should consider upgrading via the '/opt/homebrew/Cellar/jupyterlab/3.2.9/libexec/bin/python3.9 -m pip install --upgrade pip' command.
Исследуем профессию Data Scientist. Найдите как можно больше вакансий по этой профессии в Москве. Учтите, что имеет смысл искать также по другим ключевым словам, например аналитик данных.
URL = 'https://api.hh.ru/vacancies'
params_array = [{
'text': "Data Scientist",
'area': 1,
'professional_role': 10,
'per_page': 100
},
{
'text': "Аналитик данных",
'area': 1,
'professional_role': 10,
'per_page': 100
},
{
'text': "Дата инженер",
'area': 1,
'professional_role': 10,
'per_page': 100
},
{
'text': "Data analyst",
'area': 1,
'professional_role': 10,
'per_page': 100
},
{
'text': "Big data",
'area': 1,
'professional_role': 10,
'per_page': 100
}
]
jobs = []
for params in params_array:
params['page'] = 0
pages = json.loads(requests.get(URL, params).content.decode())['pages']
for i in range(pages):
params['page'] = i
req = requests.get(URL, params)
data = json.loads(req.content.decode())
jobs += data['items']
# print(*[value['name'] for value in data['items']], sep='\n') # Исследование ненужных профессий
print(f"Аналитики, нефильтрованные, с повторами: {len(jobs)}")
prohibited = ['бизнес', 'системный', 'веб', 'web', 'продукт', 'маркетолог',
'продаж', 'marketing', 'маркетинг', 'business', 'финансовый',
'product', 'sales', 'system', 'digital', 'market']
filtered = []
for job in jobs:
OK = True
for unit in prohibited:
if (unit in job['name'].lower()):
OK = False
if (OK):
filtered += [job]
jobs = filtered
print(f"Аналитики, фильтрованные, с повторами: {len(jobs)}")
Аналитики, нефильтрованные, с повторами: 3135 Аналитики, фильтрованные, с повторами: 1781
В полученную выборку некоторые вакансии могли попасть несколько раз. Удалите дубликаты.
jobs_df = pd.json_normalize(jobs)
jobs_df.drop_duplicates(subset='id', inplace=True, ignore_index = True)
print(f"Аналитики, фильтрованные, без повторов: {len(jobs_df)}")
Аналитики, фильтрованные, без повторов: 1328
print(*list(jobs_df['name']), sep='\n') # Больше негативных паттернов не нашлось, данные стали чище
Загрузите подробное описание каждой вакансии и создайте удобную таблицу данных.
descriptions = []
for i in range(len(jobs_df)):
vacancy = jobs_df['id'].iloc[i]
vacancy_url = f'https://api.hh.ru/vacancies/{vacancy}'
req = requests.get(vacancy_url)
response = json.loads(req.content.decode())
try:
descriptions += [response]
check = response['description']
print(f"Status: {i}, description: {check[:50]}")
except Exception:
print(f"Failed response: {response}")
raise
final = jobs_df.merge(pd.json_normalize(descriptions), left_on='id', right_on='id', suffixes=[None, '_remove'])
final.drop([column for column in final.columns if '_remove' in column], axis=1, inplace=True)
Проверим, что остались не бесполезные поля:
columns = final.isna().sum()
to_drop = columns[columns == len(final)]
print(to_drop)
jobs = final.drop(columns=to_drop.keys())
department 1328 address 1328 sort_point_distance 1328 insider_interview 1328 contacts 1328 salary 1328 address.description 1328 address.metro 1328 employer.logo_urls 1328 vacancy_constructor_template 1328 negotiations_url 1328 suitable_resumes_url 1328 test 1328 vacancy_constructor_template.bottom_picture 1328 dtype: int64
Полученную таблицу необходимо сохранить в формате xlsx и отправить боту вместе с решением.
import openpyxl
jobs_to_save = jobs.drop(columns='branded_description') # Важно, чтобы в excel было читабельно
jobs_to_save.to_excel("jobs.xlsx")
Вопрос 1. Сколько сейчас доступно вакансий по вашему запросу?
jobs = pd.read_excel('jobs.xlsx')
print(f"Доступно вакансий: {len(jobs)}")
Доступно вакансий: 1328
Вопрос 2. Какие навыки чаще всего встречаются в вакансиях по данной специальности?
Для этого найдите соответствующее поле в данных, проанализируйте его и составьте список навыков и количество упоминаний каждого. Визуализируйте полученную информацию по топ-15 навыков.
import ast
SHOW_SIZE = 15
skills_freq = dict()
skills_arr = list(jobs['key_skills'])
for skills in skills_arr:
skills_json = ast.literal_eval(skills)
for skill in skills_json:
skill_name = skill['name']
if (skill_name not in skills_freq):
skills_freq[skill_name] = 0
skills_freq[skill_name] += 1
items = sorted(skills_freq.items(), key=lambda item: item[1], reverse=True)
values = [x[1] for x in items]
labels = [x[0] for x in items]
fig = plt.figure(figsize=(12, 12))
ax = plt.axes()
size = sum(values[:SHOW_SIZE])
actual_size = len(skills_arr)
ax.barh(np.arange(SHOW_SIZE), values[:SHOW_SIZE][::-1]);
ax.set_yticks(np.arange(SHOW_SIZE), labels=labels[:SHOW_SIZE][::-1])
ax.set_title("Топ востребованных навыков в Data science");
ax.set_xlabel("Количество вакансий");
Вопрос 3. Какую зарплату готовы платить работодатели? Соберите некоторым образом статистику и постройте гистограмму.
При работе с данными о заработной плате обратите внимание на валюту и gross/net.
jobs['salary.currency'].drop_duplicates()
0 RUR 1 NaN 14 EUR 20 USD Name: salary.currency, dtype: object
def get_salary(obj):
ratio = {'RUR': 1, 'EUR': 131, 'USD': 120}
tax_ratio = 0.87
return obj['salary.from'] * ratio[obj['salary.currency']] * (tax_ratio if obj['salary.gross'] else 1)
salaries = list(jobs.reindex(columns=['salary.currency', 'salary.from', 'salary.gross']).dropna().apply(get_salary, axis=1))
fig = plt.figure(figsize=(12, 12))
ax = plt.axes()
ax.hist(salaries, bins=40)
ax.set_xlim((0, 350000));
ax.set_ylabel('Количество вакансий')
ax.set_xlabel('Заработная плата (RUR), "чистыми"')
ax.set_title('Распределение ЗП среди аналитиков данных');
Выводы: SQL, Python и аналитическое мышление являются ключевыми навыками. Заметны просадки в зарплате в районе 90-99 тыс. и 190-199 тыс. Это можно объяснить попыткой работодателей увеличить "порядок" зарплаты. Также было полезно проанализировать поля датасета и получить более точные данные по зарплате: удалось перевести все цифры в net зарплату в рублях. Также было полезно сохранять датасеты, чтобы восстанавливаться из них, а не парсить вновь.
Проведите аналогичный анализ для наиболее привлекательной для вас профессии в любом регионе. Если это Data Scientist, то для анализа выберите другую.
URL = 'https://api.hh.ru/vacancies'
params_array = [{
'text': "Python Developer",
'area': 2,
'professional_role': 96,
'per_page': 100
},
{
'text': "Разработчик Python",
'area': 2,
'professional_role': 96,
'per_page': 100
},
{
'text': "Backend Developer Python",
'area': 2,
'professional_role': 96,
'per_page': 100
},
{
'text': "Software Developer Python",
'area': 2,
'professional_role': 96,
'per_page': 100
}
]
jobs = []
for params in params_array:
params['page'] = 0
pages = json.loads(requests.get(URL, params).content.decode())['pages']
for i in range(pages):
params['page'] = i
req = requests.get(URL, params)
data = json.loads(req.content.decode())
jobs += data['items']
# print(*[value['name'] for value in data['items']], sep='\n') # Исследование ненужных профессий
print(f"Разработчики, нефильтрованные, с повторами: {len(jobs)}")
prohibited = ['java', 'devops', 'frontend', 'c++', 'js',
'c#', 'scientist', '1с', 'web', 'с++', 'go',
'ruby', '.net']
filtered = []
for job in jobs:
OK = True
for unit in prohibited:
if (unit in job['name'].lower()):
OK = False
if (OK):
filtered += [job]
jobs = filtered
print(f"Разработчики, фильтрованные, с повторами: {len(jobs)}")
jobs_df = pd.json_normalize(jobs)
jobs_df.drop_duplicates(subset='id', inplace=True, ignore_index = True)
print(f"Разработчики, фильтрованные, без повторов: {len(jobs_df)}")
Разработчики, нефильтрованные, с повторами: 1832 Разработчики, фильтрованные, с повторами: 1190 Разработчики, фильтрованные, без повторов: 526
Проверим, кто остался:
print(*list(jobs_df['name']), sep='\n') # Больше негативных паттернов не нашлось, данные стали чище
Загрузим вакансии:
descriptions = []
for i in range(len(jobs_df)):
vacancy = jobs_df['id'].iloc[i]
vacancy_url = f'https://api.hh.ru/vacancies/{vacancy}'
req = requests.get(vacancy_url)
response = json.loads(req.content.decode())
try:
descriptions += [response]
check = response['description']
print(f"Status: {i}, description: {check[:50]}")
except Exception:
print(f"Failed response: {response}")
raise
final = jobs_df.merge(pd.json_normalize(descriptions), left_on='id', right_on='id', suffixes=[None, '_remove'])
final.drop([column for column in final.columns if '_remove' in column], axis=1, inplace=True)
Проверим, что остались не бесполезные поля:
columns = final.isna().sum()
to_drop = columns[columns == len(final)]
print(to_drop)
jobs = final.drop(columns=to_drop.keys())
department 526 response_url 526 sort_point_distance 526 insider_interview 526 contacts 526 address.description 526 address.metro 526 salary 526 address 526 employer.logo_urls 526 vacancy_constructor_template 526 negotiations_url 526 suitable_resumes_url 526 test 526 dtype: int64
Вопрос 1. Сколько сейчас доступно вакансий по вашему запросу?
jobs = pd.read_excel('jobs_python.xlsx')
print(f"Доступно вакансий: {len(jobs)}")
Доступно вакансий: 526
Вопрос 2. Какие навыки чаще всего встречаются в вакансиях по данной специальности?
Для этого найдите соответствующее поле в данных, проанализируйте его и составьте список навыков и количество упоминаний каждого. Визуализируйте полученную информацию по топ-15 навыков.
import ast
SHOW_SIZE = 15
skills_freq = dict()
skills_arr = list(jobs['key_skills'])
for skills in skills_arr:
skills_json = ast.literal_eval(skills)
for skill in skills_json:
skill_name = skill['name']
if (skill_name not in skills_freq):
skills_freq[skill_name] = 0
skills_freq[skill_name] += 1
items = sorted(skills_freq.items(), key=lambda item: item[1], reverse=True)
values = [x[1] for x in items]
labels = [x[0] for x in items]
fig = plt.figure(figsize=(12, 12))
ax = plt.axes()
size = sum(values[:SHOW_SIZE])
actual_size = len(skills_arr)
ax.barh(np.arange(SHOW_SIZE), values[:SHOW_SIZE][::-1]);
ax.set_yticks(np.arange(SHOW_SIZE), labels=labels[:SHOW_SIZE][::-1])
ax.set_title("Топ востребованных навыков в Data science");
ax.set_xlabel("Количество вакансий");
Вопрос 3. Какую зарплату готовы платить работодатели? Соберите некоторым образом статистику и постройте гистограмму.
При работе с данными о заработной плате обратите внимание на валюту и gross/net.
jobs['salary.currency'].drop_duplicates()
0 RUR 2 NaN 5 USD 10 EUR Name: salary.currency, dtype: object
def get_salary(obj):
ratio = {'RUR': 1, 'EUR': 131, 'USD': 120}
tax_ratio = 0.87
return obj['salary.from'] * ratio[obj['salary.currency']] * (tax_ratio if obj['salary.gross'] else 1)
salaries = list(jobs.reindex(columns=['salary.currency', 'salary.from', 'salary.gross']).dropna().apply(get_salary, axis=1))
fig = plt.figure(figsize=(12, 12))
ax = plt.axes()
ax.hist(salaries, bins=50)
ax.set_xlim((0, 350000));
ax.set_ylabel('Количество вакансий')
ax.set_xlabel('Заработная плата (RUR), "чистыми"')
ax.set_title('Распределение ЗП среди python разработчиков');
import openpyxl
jobs_to_save = jobs.drop(columns='branded_description') # Важно, чтобы в excel было читабельно
jobs_to_save.to_excel("jobs_python.xlsx")
Выводы: Ожидаемо владение Python, Linux, Git и SQL являются ключевыми навыками. Заметны просадки в зарплате в районе 90-99 тыс., 140-149 тыс., 190-199 тыс., 240-249 тыс. Это можно объяснить попыткой работодателей увеличить "порядок" зарплаты. Зарплаты разработчиков в Санкт-Петербурге значительно больше, чем аналитиков в Москве. Это можно объяснить тем, что в первую выборку попало достаточно вакансий аналитиков без владения инструментами Data Science.
Для одной из рассмотренных ранее профессий ответьте на следующие вопросы:
Не забудьте про визуализацию, для этого используйте библиотеку plotly. В частности, постройте распределений вакансий на карте.
Для построения последних двух графиков вам могут понадобиться внешние данные.
Рассмотрим аналитиков.
import sys
!{sys.executable} -m pip install plotly==5.6.0
import plotly
import plotly.graph_objects as go
import plotly.express as px
import plotly.offline as pyo
jobs = pd.read_excel('jobs.xlsx')
Requirement already satisfied: plotly==5.6.0 in /opt/homebrew/Cellar/jupyterlab/3.2.9/libexec/lib/python3.9/site-packages (5.6.0)
Requirement already satisfied: tenacity>=6.2.0 in /opt/homebrew/Cellar/jupyterlab/3.2.9/libexec/lib/python3.9/site-packages (from plotly==5.6.0) (8.0.1)
Requirement already satisfied: six in /opt/homebrew/Cellar/six/1.16.0_2/lib/python3.9/site-packages (from plotly==5.6.0) (1.16.0)
WARNING: You are using pip version 21.3.1; however, version 22.0.4 is available.
You should consider upgrading via the '/opt/homebrew/Cellar/jupyterlab/3.2.9/libexec/bin/python3.9 -m pip install --upgrade pip' command.
print("Возможные значения: ", list(jobs['experience.name'].drop_duplicates().values))
print("Количество пропусков: ", jobs['experience.name'].isna().sum())
Возможные значения: ['От 3 до 6 лет', 'От 1 года до 3 лет', 'Нет опыта', 'Более 6 лет'] Количество пропусков: 0
pyo.init_notebook_mode()
exp = jobs.rename(columns={'experience.name': 'Опыт работы'})
exp['Количество'] = 1
fig = px.pie(exp, values='Количество', names='Опыт работы', title="Необходимый опыт работы для аналитика")
pyo.iplot(fig, filename = 'basic-line')
employers = jobs.groupby(by='employer.name').count()['id'].sort_values(ascending=False)
values = list(employers[:10].values)[::-1]
labels = list(employers[:10].keys())[::-1]
fig = go.Figure(go.Bar(x=values, y=labels, orientation='h'))
fig.update_layout(title_text='Крупнейшие работодатели для аналитиков',
xaxis_title='Количество вакансий')
pyo.iplot(fig, filename = 'basic-line')
import plotly.express as px
px.set_mapbox_access_token('pk.eyJ1Ijoib3JhbmdlZ29kIiwiYSI6ImNsMHNpcDd0cjA0cXozYm4xb21uZjZ3czkifQ.98Jxh6dRK8VaxKkURGTQeA')
metro_stations = jobs.groupby(by=['address.metro.lat', 'address.metro.lng', 'address.metro.station_name']).count()['id']
metro_stations = metro_stations.reset_index().rename(columns={"id": "Вакансий"})
fig = px.scatter_mapbox(metro_stations, lat="address.metro.lat", lon="address.metro.lng",
text="address.metro.station_name", size="Вакансий", zoom=10.5,
hover_name="address.metro.station_name",
hover_data={"address.metro.lat": False,
"address.metro.lng": False,
"address.metro.station_name": False},
center=dict(lat=55.74, lon=37.62))
fig.update_layout(title_text='Вакансии для аналитиков')
pyo.iplot(fig, filename = 'basic-line')
Какие выводы можно сделать из построенных графиков?
Выводы: зачастую для аналитика в Москве достаточно минимального опыта работы (1-3 года). Крупнейший работодатель в этой сфере - Сбербанк. Наибольшим спросом вакансия аналитика пользуется в районах станций метро: Выставочная, Деловой центр, Технопарк и Бауманская, а также вдоль Ленинградского проспекта: станции Динамо, Белорусская, Аэропорт. Наименьшим спросом - на окраинах: станции Соколинная Гора, Бабушкинакая, Покровское, а также в центре Москвы: станции Китай-город, Смоленская, Таганская, Полянка.